

package data_queuing;

import thes2sim.Frame;
import thes2sim.*;

import com.aliasi.util.BoundedPriorityQueue;
import java.io.IOException;
import java.util.Comparator;
import java.util.logging.Level;
import java.util.logging.Logger;


public class RRobinQueuingAlgo {
    
    private Comparator<Frame> comparator = new PriorityComparator();
    BoundedPriorityQueue<Frame> dqHigh;
    BoundedPriorityQueue<Frame> dqMid;
    BoundedPriorityQueue<Frame> dqLow;
    int quantumHigh = Util.RRQAhighCount;
    int quantumMid = Util.RRQAmidCount;
    int quantumLow = Util.RRQAlowCount;
    
    Logging l = new Logging(3);
    int time;
    
    public RRobinQueuingAlgo() {
        
        dqHigh = new BoundedPriorityQueue<Frame>(comparator, Util.queueSize);
        dqMid = new BoundedPriorityQueue<Frame>(comparator, Util.queueSize);
        dqLow = new BoundedPriorityQueue<Frame>(comparator, Util.queueSize);

    }
    
    public void printQContent() {
        
       Frame f;
  
       while(!dqHigh.isEmpty()) {
           f = dqHigh.remove();
           logAction(f, 5);
       }
    
       while(!dqMid.isEmpty()) {
           f = dqMid.remove();
           logAction(f, 6);
       }
    
       while(!dqLow.isEmpty()) {
           f = dqLow.remove();
           logAction(f, 7);
       }
       
    }
    
    // Checking queue if full and discards frame at tail
    public void checkQueue (BoundedPriorityQueue<Frame> dq) {
        
        if(dq.size() >= dq.maxSize()) {
            Frame discarded = dq.last();
            discarded.setDiscTime(time);
            logAction(discarded, 2);
            dq.remove(discarded);
        }
    
    }
    
    // Determines which queue is still up for transmission
    public int checkCounters() {
    
        int i = 0;  // PrioVar
        
        // reset counters back to default values
        if(quantumHigh == 0 && quantumMid == 0 && quantumLow == 0) {
            quantumHigh = Util.RRQAhighCount;
            quantumMid = Util.RRQAmidCount;
            quantumLow = Util.RRQAlowCount;
        }

        // High queue's turn until counter is zero
        if(!dqHigh.isEmpty() && quantumHigh > 0) {
            i = 1;
            quantumHigh--;
        }
        
        // Mid queue's turn until counter is zero
        else if(!dqMid.isEmpty() && quantumMid > 0) {
            i = 2;
            quantumMid--;
        }
        
        // Low queue's turn until counter is zero
        else if(!dqLow.isEmpty() && quantumLow > 0) {
            i = 3;
            quantumLow--;
        }
        
        
        return i;
    }
    
    // Returns frame for transmission
    public Frame transmitFrame(int simTime) {
    
        Frame f = null;
        this.time = simTime;
        
        switch(checkCounters()){
            
            case 1: f = dqHigh.remove();
                    break;
            case 2: f = dqMid.remove();
                    break;
            case 3: f = dqLow.remove();
                    break;
        }
        
        // Set exit time of frame
        if (f != null) {
            f.setExtTime(time);
            logAction(f, 3);
        }

        return f;
    
    }
    
    
    // Adds frame to queue according to priority
    public void queuePDU (PDU u, int id, int simTime) {
        
        Frame f = new Frame(u, id);
        
        this.time = simTime;
        f.setEntTime(time);
        
        switch(f.getPayload().getPriority()) {
            
            // High Priority
            case 1: 
                        logAction(f, 1);
                        dqHigh.offer(f);
                        checkQueue(dqHigh);
                        break;
            
            // Mid Priority    
            case 2:
                        logAction(f, 1);
                        dqMid.offer(f);
                        checkQueue(dqMid);
                        break;
            
            // Low Priority
            case 3:
                        logAction(f, 1);
                        dqLow.offer(f);
                        checkQueue(dqLow);
                        break;
                
        }
        
    }
    
    
    // Sorting frames according to time creation
    private class PriorityComparator implements Comparator<Frame> {

        public int compare(Frame x, Frame y) {
            
            if(x.isServicing()) {
                return 1;
            }
            
            if(x.getPayload().getTimecreate() < y.getPayload().getTimecreate()) {
                return 1;
            }
            
            if(x.getPayload().getTimecreate() > y.getPayload().getTimecreate()) {
                return -1;
            }
            
            return 0;
        }

    }
    
    // 1 - inserted frame
    // 2 - discarded frame
    // 3 - transmitted frame
    // 4, 5, 6, 7 - queue content
    
    public void logAction(Frame f, int type) {
        
        try {
            
            switch(type){
                
                case 1:
                    l.logActivity(
                        time + ", " 
                        + "Frame Insert, " 
                        + f.getId() + ", "
                        + f.getPayload().getPriority() + ", "
                        + f.getPayload().getTimecreate(), 1);

                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority() + ", " 
                                + f.getEntTime(), 2);
                    break;
                    
                case 2:
                    l.logActivity(
                        time + ", " 
                        + "Frame Discarded, " 
                        + f.getId() + ", "
                        + f.getPayload().getPriority() + ", "
                        + f.getPayload().getTimecreate(), 1);
                    
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority() + ", " 
                                + f.getEntTime() + ", " 
                                + f.getDiscTime(), 3);
                    break;
                    
                case 3:
                    l.logActivity(
                        time + ", " 
                        + "Frame Transmitted, " 
                        + f.getId() + ", "
                        + f.getPayload().getPriority() + ", "
                        + f.getPayload().getTimecreate(), 1);
                    
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority() + ", " 
                                + f.getEntTime() + ", " 
                                + f.getExtTime(), 4);
                    break;
                    
                //case 4:
                //    l.logActivity(f.getId() + ", " + f.getPayload().getPriority(), 5);
                //    break;
                
                case 5:
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority(), 6);
                    break;

                    
                case 6:
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority(), 7);
                    break;
                    
                case 7:
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority(), 8);
                    break;
                    
            }
            
        } catch (IOException ex) {
            Logger.getLogger(QueuingOrig.class.getName()).log(Level.SEVERE, null, ex);
        }
       

    }
    
    
}
